//
// Created by xinrong.qian on 2025/5/29.
//

#include <iostream>
#include <opencv2/highgui.hpp>
#include "processor/CardParamPickProcessor.h"
#include "processor/QRPickDetector.h"
#include "processor/CardParam.h"

/********************************************************/
// TcResponseInfo封装错误处理逻辑
TcResponseInfo
CardParamPickProcessor::ErrorResponseInfo(const std::string& errorMsg) {
    std::cerr << errorMsg << std::endl;
    return {errorMsg, "", {}, 0, 0, 0, 0, 0};
}

// TcResponseInfo封装成功返回函数
TcResponseInfo
CardParamPickProcessor::SuccessResponseInfo(const std::string& successMsg, const std::string& tcResult,
                                   const std::vector<double>& grayValues, int pc, int pt,
                                   double cValue, double tValue, double tcRatio) {
    return {successMsg, tcResult, grayValues, pc, pt, cValue, tValue, tcRatio};
}

std::string
CardParamPickProcessor::calculateTcResult(double tcRatio) {
    if (tcRatio == -1 || tcRatio == 0 || tcRatio == 0.01) {
        return "无效"; // 无效
    } else if (tcRatio < 0.85) {
        return "阳性"; // 阳性
    } else if (tcRatio <= 1.0) {
        return "弱阳性"; // 弱阳性
    } else {
        return "阴性"; // 阴性
    }
}


cv::Mat
CardParamPickProcessor::correctAndCropWhiteRectangle(const cv::Mat &input) {
    // 直接返回原始图像如果输入无效
    if (input.empty())  {
        return input.clone();  // 返回深拷贝
    }

    // 创建处理用的副本
    cv::Mat processed = input.clone();
    cv::Mat grey;
    cv::cvtColor(processed, grey, cv::COLOR_BGR2GRAY);

    // 二值化处理
    cv::threshold(grey, grey, 0, 255, cv::THRESH_BINARY | cv::THRESH_OTSU);

    // 形态学开运算去除小噪点
    {
        cv::Mat kernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5));
        cv::morphologyEx(grey, grey, cv::MORPH_OPEN, kernel);
        // kernel自动释放
    }

    // 寻找轮廓
    std::vector<std::vector<cv::Point>> contours;
    std::vector<cv::Vec4i> hierarchy;
    cv::findContours(grey, contours, hierarchy, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);

    // 寻找符合条件的最大轮廓
    auto isValidContour = [](const std::vector<cv::Point>& contour) {
        cv::Rect rect = cv::boundingRect(contour);
        return rect.width  < rect.height;  // 只考虑宽度小于高度的轮廓
    };

    auto it = std::max_element(contours.begin(),  contours.end(),
                               [&](const std::vector<cv::Point>& a, const std::vector<cv::Point>& b) {
                                   return !isValidContour(a) || (isValidContour(b) && cv::contourArea(a) < cv::contourArea(b));
                               });

    if (it != contours.end()  && isValidContour(*it)) {
        // 获取最小外接旋转矩形
        cv::RotatedRect rotatedRect = cv::minAreaRect(*it);

        // 计算旋转角度（确保高度大于宽度）
        float angle = rotatedRect.angle;
        if (rotatedRect.size.width  > rotatedRect.size.height)  {
            angle += 90.0f;
        }

        // 获取旋转矩阵
        cv::Mat rotMatrix = cv::getRotationMatrix2D(rotatedRect.center,  angle, 1.0);

        // 旋转图像
        cv::Mat rotated;
        cv::warpAffine(processed, rotated, rotMatrix, processed.size(),
                       cv::INTER_LINEAR, cv::BORDER_CONSTANT, cv::Scalar(0,0,0));

        // 计算旋转后的边界框
        std::vector<cv::Point2f> rectPoints(4);
        rotatedRect.points(rectPoints.data());

        std::vector<cv::Point2f> transformedPoints;
        cv::transform(rectPoints, transformedPoints, rotMatrix);

        cv::Rect bbox = cv::boundingRect(transformedPoints);
        cv::Rect safeROI = bbox & cv::Rect(0, 0, rotated.cols,  rotated.rows);

        // 返回裁剪后的图像
        return rotated(safeROI).clone(); // 确保返回的是独立拷贝
    }

    // 没有找到有效轮廓，返回原始图像副本
    return input.clone();
}



/**
 * 绘制无旋转矩形
 * @param image 输入图片
 * @param center 矩形中心点坐标 (x,y)
 * @param width 矩形宽度
 * @param height 矩形高度

*/
void
CardParamPickProcessor::visualizeRect(cv::Mat& image,
                                 const cv::Point2f &center,
                                 cv::Scalar_<double> color,
                                 double width,
                                 double height){
    //  * @param color 矩形颜色
    // * @param thickness 矩形线条宽度
    int thickness = 1;

    cv::Point pt1(center.x - width/2, center.y - height/2);
    cv::Point pt2(center.x + width/2, center.y + height/2);
    cv::rectangle(image, pt1, pt2, color, thickness);
}

// 绘制折线图
void
CardParamPickProcessor::drawCurve(const std::vector<double> &data, std::string &name) {
    if (data.empty()) {
        std::cout << "输入的数据为空，无法绘制曲线。" << std::endl;
        return;
    }

    cv::Mat image = cv::Mat::zeros(500, 800, CV_8UC3);

    int width = image.cols;
    int height = image.rows;

    // 找到数据的最大值和最小值
    double minVal, maxVal;
    cv::minMaxLoc(data, &minVal, &maxVal);

    // 计算坐标映射的比例
    double scaleX = static_cast<double>(width) / (data.size() - 1);
    double scaleY = static_cast<double>(height) / (maxVal - minVal);

    // 绘制曲线
    for (size_t i = 0; i < data.size() - 1; ++i) {
        int x1 = static_cast<int>(i * scaleX);
        int y1 = height - static_cast<int>((data[i] - minVal) * scaleY);
        int x2 = static_cast<int>((i + 1) * scaleX);
        int y2 = height - static_cast<int>((data[i + 1] - minVal) * scaleY);

        cv::line(image, cv::Point(x1, y1), cv::Point(x2, y2), cv::Scalar(0, 255, 0), 2);
    }

    // 显示图像
    cv::imshow(name, image);
}


// 声明扫描图像均值计算
std::vector<double>
CardParamPickProcessor::meanRoundRectangle(const cv::Mat &imgGray, int R) {
    int width = imgGray.cols;
    int height = imgGray.rows;

    std::vector<double> outVector;

    // 目标是在整个高度范围内检测每一行，
    // 对每一行中的每个点进行半径 R 的画圈，只计算圆内的区域面积，然后对每一行的结果求和。
    // 最后，继续处理下一行，直到处理完所有行。
    /*************************开始扫描**************************/
    for (int a = 0; a < height; ++a) {
        int sum_pix = 0;
        int pix_counts = 0;

        int i_min = std::max(a - R, 0);
        int i_max = std::min(a + R, height - 1);

        // 规定height方向范围
        for (int i = i_min; i <= i_max; ++i) {
            int dy = i - a;
            int dy2 = dy * dy;

            // 规定width方向范围
            int j_min = std::max(0, R - std::abs(dy));
            int j_max = std::min(width - 1, width - R + std::abs(dy));

            for (int j = j_min; j <= j_max; ++j) {
                int dx = j - R;
                int r2 = dx * dx + dy2;

                if (r2 <= R * R) {
                    sum_pix += 255 - imgGray.at<uchar>(i, j);
                    ++pix_counts;
                }
            }
        }

        if (pix_counts > 0) {
            outVector.push_back(static_cast<double>(sum_pix) / static_cast<double>(pix_counts));
        } else {
            outVector.push_back(0.0);  // 如果没有像素点在圆内，添加0.0
        }
    }
    /*************************结束扫描**************************/
    return outVector;
}

//预处理图片
cv::Mat
CardParamPickProcessor::preprocessImage(const cv::Mat& src) {
// --- 1. 灰度化 ---
    // --- 1. 灰度化（自定义权重，提高红色比重）---
    cv::Mat gray;
    cvtColor(src, gray, cv::COLOR_BGR2GRAY); // 默认使用公式: Y = 0.299R + 0.587G + 0.114B

    return gray;
}

std::tuple<int, int, int> CardParamPickProcessor::FindPeakPoints(const std::vector<double> &grayValues) {
    if (grayValues.empty()) {
        return {-1, -1, -1};
    }

    int n = grayValues.size();
    int mid = n / 2;

    // 辅助函数：寻找指定范围内的最大值索引
    auto findMaxPeak = [](const std::vector<double> &values, int start, int end) -> int {
        if (start >= end) return -1;
        int maxIdx = start;
        double maxValue = values[start];
        for (int i = start; i < end; ++i) {
            if (values[i] > maxValue) {
                maxValue = values[i];
                maxIdx = i;
            }
        }
        return maxIdx;
    };

    int c_start = n / 5;
    int c_end = n / 2;

    int t_start = n / 2;
    int t_end = n / 5 * 4;

    // 查找前半段最大值（0到mid）
    int peak1 = findMaxPeak(grayValues, c_start, c_end);

    // 查找后半段最大值（mid到n）
    int peak2 = findMaxPeak(grayValues, t_start, t_end);

    // 检查是否存在有效波峰
    if (peak1 == -1 || peak2 == -1) {
        return {-1, -1, -1};
    }

    return {peak1, peak2, mid};
}

// 截取复制曲线数据
std::vector<double>
CardParamPickProcessor::crop(const std::vector<double> &data, int start, int end) {
    int size = data.size();
    if (end > size) {
        end = size;

    }
    if (start > size) {
        start = size;
        end = size;

        return std::vector<double>();
    }
    return std::vector<double>(data.begin() + start, data.begin() + end);
}

// 曲线内找最小值
cv::Point2d
CardParamPickProcessor::minPoint2d(const std::vector<double> &average) {
    int size = average.size();
    if (average.size() == 0) {
        return cv::Point2d(0, 0);
    }
    double min = average[0];
    int xmin = 0;
    for (int i(1); i < size; i++) {
        if (average[i] < min) {
            min = average[i];
            xmin = int(i);
        }
    }

    return cv::Point2d(xmin, min);
}

// 计算给定数据向量 data 在指定区间内的定积分
double
CardParamPickProcessor::getDefiniteIntegral(const std::vector<double> &data, cv::Point2d local, int hwf) {
    double out = 0;
    int start = local.x - hwf > 0 ? local.x - hwf : 0;
    int end = local.x + hwf < data.size() ? local.x + hwf : data.size();
    for (; start < end; start++) {
        out += data[start];
    }
    return out;
}



/********************************************************/

/**
 * 请求返回函数
 *
 * @param input
 * @param x
 * @param y
 * @param width
 * @param height
 * @return TcResponseInfo
 */
TcResponseInfo CardParamPickProcessor::GetTcResponseInfo(const cv::Mat &input, int x, int y, int width, int height) {
    if (input.empty())  {
        std::cerr << "The input image is empty" << std::endl;
        return ErrorResponseInfo("The input image is empty");
    }


    // 裁减图像
    cv::Mat image = correctAndCropWhiteRectangle(input);
    if (image.empty()){
        return ErrorResponseInfo("The input image is empty");
    }

//    if (debugFlag){
//        cv::imshow("image", image);
////        cv::waitKey(0);
//    }

    // 多个单卡 - 借用单卡参数
    std::vector<CTLineResult> resultsArr;
    const CardParam defaultCard = {
            .channel_height = 140,
            .channel_width = 20,
            .qr_side_length = 125,
            .qr_to_top = 85
    };

    try {
        // 创建QRPickDetector操作对象
        QRPickDetector qrDetector;

        // 检查图像中是否包含QR码
        if (!qrDetector.isContainsQRCode(image)) {
            std::cerr << "No QR code detected" << std::endl;
            return ErrorResponseInfo("No QR code detected");
        }

        // 生成QR计算的相关内部参数信息
        qrDetector.generateQRParams();
        const auto& qrContents = qrDetector.getQrContents();
        const size_t numQRCodes = qrContents.size(); // 获取QR码数量

        if (numQRCodes != 1){
            std::cerr << "Multiple QR codes detected" << std::endl;
            throw std::runtime_error("Multiple QR codes detected");
        }

        // 预分配内存
        std::vector<CardParam> cardParamsArr(numQRCodes, defaultCard);
        std::vector<double> qrFactorValues; // 存储缩放系数
        qrFactorValues.reserve(numQRCodes);

        // 计算缩放系数
        for (size_t i = 0; i < numQRCodes; ++i) {
            qrFactorValues.push_back(qrDetector.getScaleFactorValue(defaultCard.qr_side_length,  i));
        }

        // 应用缩放系数
        for (size_t i = 0; i < numQRCodes; ++i) {
            CardParam& params = cardParamsArr[i];
            const double factor = qrFactorValues[i];
            ;
            params.channel_height  *= factor;
            params.channel_width  *= factor;
            params.qr_side_length  *= factor;
            params.qr_to_top  *= factor;

            // 打印逻辑函数
            std::cout << "-----------------------------New Params-------------------------------\n"
                      << "channel_height: " << params.channel_height  << "\n"
                      << "channel_width: " << params.channel_width  << "\n"
                      << "qr_side_length: " << params.qr_side_length  << "\n"
                      << "qr_to_top: " << params.qr_to_top  << "\n"
                      << "------------------------------------------------------------\n";
        }

        // 处理ROI区域
        std::vector<cv::Mat> roiMats;
        roiMats.reserve(numQRCodes);
        for (size_t i = 0; i < numQRCodes; ++i) {
            const CardParam& params = cardParamsArr[i];
            const auto& qrCorners = qrDetector.getAllQrCorners()[i];

            const double qrBottomToChannelCenter = (params.qr_to_top)  + params.channel_height  / 2.0;
            qrDetector.processSingleQR(qrCorners,  qrBottomToChannelCenter, params.channel_width,  params.channel_height);

            roiMats.push_back(qrDetector.getExtractedROIs(i));
            if (debugFlag) {
                cv::imshow("ROI " + std::to_string(i + 1), roiMats.back());
            }
        }

        if (debugFlag) {
            cv::imshow("Marked QR Code", qrDetector.getMarkedImage());
        }

        // 扫描法计算CT线
        for (int i = 0; i < numQRCodes; ++i) {
            const cv::Mat& roi = roiMats[i];
            CTLineResult result = CalculateTc(roi);
            resultsArr.push_back(result);

            if (debugFlag){
                // 绘制灰度曲线
                std::string windowName = "Curve " + std::to_string(i + 1);
                drawCurve(result.grayValues, windowName);
            }

        }

        if (debugFlag) {
            cv::waitKey(0);
        }

        return SuccessResponseInfo(
                "Success",
                resultsArr[0].tcResult,
                resultsArr[0].grayValues,
                resultsArr[0].pc,
                resultsArr[0].pt,
                resultsArr[0].cValue,
                resultsArr[0].tValue,
                resultsArr[0].tcRatio
        );

    } catch (const std::exception &e) {
        std::cerr << "Exception: " << e.what()  << std::endl;
        return ErrorResponseInfo(e.what());
    }
}

CTLineResult CardParamPickProcessor::CalculateTc(const cv::Mat &image) {
    CTLineResult result;

    if (image.empty()) {
        throw std::runtime_error("The input image is empty");
    }

    // 预处理后的mat
    cv::Mat channelBitmap = preprocessImage(image);
    // 图片尺寸大小
    std::cout << "Image size: " << channelBitmap.rows << " x " << channelBitmap.cols << std::endl;

    std::vector<double> greyArray = meanRoundRectangle(channelBitmap, 3);

    auto [cPosition, tPosition, mPosition] = FindPeakPoints(greyArray);
    result.pc = cPosition;
    result.pt = tPosition;

    if (result.pc == -1 || result.pt == -1) {
        throw std::runtime_error("Image detection area C T line not found, please check the detection card");
    }

    std::cout << "pc: " << result.pc << std::endl;
    std::cout << "pt: " << result.pt << std::endl;
    std::cout << "mid: " << mPosition << std::endl;

    std::vector<double> ctCurve = crop(greyArray, result.pc, result.pt);
    cv::Point2d ctMidMinPoint = minPoint2d(ctCurve);

    result.grayValues.resize(greyArray.size(), 0);
    for (size_t i = 0; i < greyArray.size(); ++i) {
        result.grayValues[i] = std::max(0.0, greyArray[i] - ctMidMinPoint.y);
    }

    int hwf = 3; // 半峰宽

    bool cInvalid = true;
    bool tInvalid = true;

    // 无效卡 检测CT线位置是否在左右侧来判别
    if (result.pc < 0.25 * mPosition || result.pc > 0.75 * mPosition) cInvalid = false;
    if (result.pt < 1.25 * mPosition || result.pt > 1.75 * mPosition) tInvalid = false;

    result.cValue = cInvalid ?getDefiniteIntegral(result.grayValues,
                                                               cv::Point2d(result.pc, result.grayValues[result.pc]),
                                                               hwf) : 0;
    result.tValue = tInvalid ? getDefiniteIntegral(result.grayValues,
                                                               cv::Point2d(result.pt, result.grayValues[result.pt]),
                                                               hwf) : 0;

    if (result.cValue == 0 && result.tValue == 0) { // 1.无CT线
        result.tcRatio = -1;
        std::fill(result.grayValues.begin(), result.grayValues.end(), 0);
    } else if (result.cValue == 0) { // 2.无C线
        result.tcRatio = -1;
        std::fill(result.grayValues.begin(), result.grayValues.begin() + ctMidMinPoint.x, 0);
    } else if (result.tValue == 0) { // 3.无T线
        result.tcRatio = 0.01;
        std::fill(result.grayValues.begin() + ctMidMinPoint.x, result.grayValues.end(), 0);
    } else { // 4.正常计算
        result.tcRatio = std::round((result.tValue / result.cValue) * 100.0) / 100.0; // 计算 TC 比值 （保留两位小数）
    }

    // 阴阳性判别
    result.tcResult = calculateTcResult(result.tcRatio);

    std::cout << "c: " << result.cValue << std::endl;
    std::cout << "t: " << result.tValue << std::endl;
    std::cout << "tcRatio: " << result.tcRatio << std::endl;
    std::cout << "tcResult: " << result.tcResult << std::endl;

    channelBitmap.release();

    return result;
}
